//
//  QYSocketManager.swift
//  QYNetworkSDK
//
//  Created by blazer on 2022/8/5.
//

import Foundation

//MARK: --- QYSocketPool
//链表 -- LRU + 线程安全
public class QYSocketPool<Element: Equatable> {
    
    private let lock = NSLock.init()
    
    public typealias Node = LinkedListNode<Element>
    public class LinkedListNode<Element> {
        let element: Element
        weak var pre: LinkedListNode? = nil
        var next: LinkedListNode? = nil
        
        init(value: Element) {
            element = value
        }
    }

    private var head: Node?
    public var last: Node?{
        get {
            guard var node = head else {
                return nil
            }
            while let next = node.next {
                node = next
            }
            return node
        }
    }
    
    var count: Int {
        get {
            guard var node = head else{
                return 0
            }
            var c = 1;
            while let next = node.next {
                node = next
                c += 1
            }
            return c;
        }
    }
    
    var isEmpty: Bool {
        get {
            return head == nil
        }
    }
    
    private func objectIndex(for element: Element) -> Node? {
        guard var node = head else{
            return nil
        }
        while let next = node.next {
            node = next
            if node.element == element {
                return node
            }
        }
        return nil
    }
}

//MARK: -- QYSocketPool public method
extension QYSocketPool {
    // 从尾部添加
    public func append(_ element: Element) {
        let node = Node.init(value: element)
        if let lastNode = last {
            lock.lock()
            node.pre = lastNode
            lastNode.next = node
            lock.unlock()
        } else {
            lock.lock()
            head = node
            lock.unlock()
        }
    }
    
    // 更改位置 -- 移到头部
    public func update(_ element: Element) {
        guard let head = self.head else { //空链表
            return
        }
        if head.element == element { //就是头部
            return
        }
        guard let node = objectIndex(for: element) else { // 没有找到
            return
        }
        lock.lock()
        //移动位置
        node.pre!.next = node.next
        node.next?.pre = node.pre
        node.next = head
        head.pre = node
        node.pre = nil
        self.head = node
        lock.unlock()
    }
    
    //删除尾部
    public func removeLast() -> Element? {
        guard let node = last else {
            return nil
        }
        let e = node.element
        lock.lock()
        node.pre?.next = nil
        node.next = nil
        lock.unlock()
        return e
    }
    
    public func remove(_ element: Element) {
        guard let node = objectIndex(for: element) else {
            return
        }
        if node.pre == nil {
            lock.lock()
            node.next?.pre = nil
            node.next = nil
            lock.unlock()
        } else {
            lock.lock()
            node.pre?.next = node.next
            node.next?.pre = node.pre
            node.pre = nil
            node.next = nil
            lock.unlock()
        }
    }
}

//MARK: --- QYSocketParam
struct QYSocketParam: Equatable,Hashable {
    let host: String
    let port: Int
//    var socket: GCDAsyncSocket? = nil  /**struct中有class 是否有性能问题*/
    var isLongLinks: Bool = false
    var tag: Int = 0
    
    public static func ==(lhs: Self, rhs: Self) -> Bool {
        return lhs.tag == rhs.tag && lhs.host == rhs.host && lhs.port == rhs.port
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(host)
        hasher.combine(port)
    }
    
    var hashValue: Int {
        var hasher = Hasher()
        self.hash(into: &hasher)
        return hasher.finalize()
    }
}

//MARK: -- QYSocketManager

/** SOCKET连接池  == 哈希表+双向链表
 *  LRU策略
 *  重连机制
 *  心跳机制
 *  粘包，拆包处理
 *  长链接不能释放 -- 除非成为后台或者杀掉进程
 */
fileprivate var randomNumbers = [Int]()
public class QYSocketManager {
    public var capacity: Int = 4
    private var map: [Int: QYSocketPool<QYSocketParam>]
    
    public static let shared = QYSocketManager()
    
    init(){
        map = Dictionary.init()
    }
    
    private func delete(tag: Int) {
        if randomNumbers.contains(tag) {
            randomNumbers.remove(at: tag)
        }
    }
    
}

//MARK: public method

extension QYSocketManager {
    /** 生成tag */
    static func generateRandomNumber() -> Int {
        let number = Int.random(in: 101...1001)
        if randomNumbers.contains(number) {
            return QYSocketManager.generateRandomNumber()
        } else {
            randomNumbers.append(number)
            return number
        }
    }
    
    // 是否已满
    func isFull(_ host: String, port: Int) -> Bool{
        let param = QYSocketParam(host: host, port: port)
        guard let linked = map[param.hashValue] else {
            return false
        }
        
        if linked.count >= capacity {
            return true
        } else {
            return false
        }
    }
    
    // 必须调用isFull
    func getSocket(_ host: String, port: Int) -> QYSocketParam {
        let param = QYSocketParam(host: host, port: port)
        guard let linked = map[param.hashValue] else {
            return QYSocketParam(host: host, port: port, tag: QYSocketManager.generateRandomNumber())
        }
        
        guard linked.count >= capacity else {
            return QYSocketParam(host: host, port: port, tag: QYSocketManager.generateRandomNumber())
        }
        
        return linked.last!.element
    }
    
    func add(socket param: QYSocketParam) {
        let linked = map[param.hashValue]
        if linked == nil {
            let pool = QYSocketPool<QYSocketParam>.init()
            pool.append(param)
            map[param.hashValue] = pool
        } else {
            if linked!.count < capacity  {
                linked?.append(param)
            }
        }
    }
    
    func update(socket param: QYSocketParam) {
        guard let linked = map[param.hashValue] else {
            return
        }
        linked.update(param)
    }
    
    func remove(socket param: QYSocketParam) {
        guard let linked = map[param.hashValue] else {
            return
        }
        linked.remove(param)
    }
}
